using System;
using System.Collections.Generic;

namespace Mbs.ComponentModel
{
    class Transaction : UndoAction, ITransaction
    {
        private List<IUndoAction> actions;
        private ActionStack actionStack;
        private TransactionStatus status;
        internal int transactionCount = 1;

        internal Transaction(string name, ActionStack actionStack)
        {
            Name = name;
            this.actionStack = actionStack;
            actions = new List<IUndoAction>();
            status = TransactionStatus.Unkown;
        }

        public TransactionStatus Status
        {
            get { return status; }
        }

        public void Commit()
        {
            transactionCount--;

            if (transactionCount == 0
                && status == TransactionStatus.Init
                && actions.Count > 0)
            {
                actionStack.Push(this);
                status = TransactionStatus.Commited;
            }
        }

        public void Rollback()
        {
            transactionCount = 0;

            Undo();
            Clear();
            status = TransactionStatus.Rollbacked;
        }

        public void Enlist()
        {
            transactionCount++;
        }


        public void AddAction(IUndoAction action)
        {
            if (action == null)
                throw new ArgumentNullException("action", "action == null");

            if (status == TransactionStatus.Commited
                || status == TransactionStatus.Rollbacked)
                throw new InvalidOperationException("invalid transaction status:" + status.ToString());

            if (!actions.Contains(action))
            {
                actions.Add(action);
                status = TransactionStatus.Init;
            }
        }

        public override void Undo()
        {
            for (int i = actions.Count - 1; i >= 0; i--)
            {
                try
                {
                    actions[i].Undo();
                }
                catch (Exception ex)
                {
                    ActionStack._log.Error(ex.Message, ex);
                }
            }
        }

        public override void Redo()
        {
            foreach (IUndoAction action in actions)
            {
                try
                {
                    action.Redo();
                }
                catch (Exception ex)
                {
                    ActionStack._log.Error(ex.Message, ex);
                }
            }
        }

        public int Count
        {
            get { return actions.Count; }
        }

        public void Clear()
        {
            foreach (IUndoAction item in actions)
            {
                if (item is IDisposable)
                    ((IDisposable)item).Dispose();
            }
            actions.Clear();
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            Clear();
        }
    }
}
